/**
 * @file main/main.c
 * @brief main function and dynamic loader code
 * @author Bojan Nesovic
 * @date 2013-11-14
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <time.h>
#include <sys/time.h>

#include "../util/ping.h"
#include "../util/nmap.h"


typedef enum {
  OPTION_UNKNOWN,
  OPTION_PING,
  OPTION_NMAP
} Option;


/**
 * @brief get time in ms
 * @return time in ms
 */
int GetTimeMilliSec(void) {
  struct timespec t;
 
  clock_gettime(CLOCK_MONOTONIC, &t);
  
  return (t.tv_sec * 1000 + t.tv_nsec / 1000000);
}


typedef int (*fptr)();

/**
 * @brief load shared libary and get back function pointer
 * 
 * @param name libary name
 * @return function pointer as (void*)
 */
fptr LoadLib(const char *name) {
  char path[1024];
  void *handle = NULL; /* for file handling */
  fptr fct = NULL; /* for function handling */
  char *error = NULL; /* for error handling */

  /* build full path */
  snprintf(path, sizeof(path), "../util/%s.so", name);

  /* open libary */
  handle = dlopen(path, RTLD_NOW);

  /* check for error */
  if ((error = dlerror())) {
    /* print errors */
    fprintf(stderr, "Couldn't load shared libary '%s'\n", name);
    fprintf(stderr, "%s\n", error);
    return NULL;
  }

  if ((fct = (fptr)dlsym(handle, name)) == NULL) {
    fprintf(stderr, "Couldn't find function '%s' in libary\n", name);
  }

  return fct;
}

/**
 * @brief help in case of wrong usage
 * 
 * @param prog programm name
 * @return void
 */
void printHelp(const char *prog) {
  fprintf(stderr, "Usage: %s [-i inputfile] [-o outputfile] [-t scan/avail] [-k count] [-s rtt] [-p time profiling] \n", prog);
  exit(EXIT_FAILURE);
}

/**
 * @brief main function
 * 
 * @param argc argument counter
 * @param argv arguments
 * @return int
 */
int main(int argc, char *argv[]) {
  fptr fct;
  extern char *optarg;
  char *inputfilename = NULL, *outputfilename = NULL;
  FILE *inputfile = NULL, * outputfile = NULL;
  int opt = 0, pingcount = 0, nsec = 0, ntime = 0;
  char line[1024];
  int start = 0, stop = 0;

  Option option = OPTION_UNKNOWN;

  while ((opt = getopt(argc, argv, "i:o:t:k:sp")) != -1) {
    switch (opt) {
        /**inputfile*/
      case 'i':
        inputfilename = optarg;
        /*printf("\n-i found %s\n", inputfilename);*/
        break;

        /**outputfile*/
      case 'o':
        outputfilename = optarg;
        /*printf("-o found %s\n", outputfilename);*/
        break;
		/**tooloption*/
      case 't':

        /* option was ping? */
        if (strcmp(optarg, "avail") == 0) {
          option = OPTION_PING;
        } else if (strcmp(optarg, "scan") == 0) {
          option = OPTION_NMAP;
        }

        break;

        /**count for icmp requests*/
      case 'k':
        pingcount = atoi(optarg);
        /*printf("-k found %d\n\n", pingcount);*/
        break;

        /**rtt*/
      case 's':
        nsec = 1;
        /*printf("s found");*/
        break;

        /**timer*/
      case 'p':
        ntime = 1;
        /*printf("p found");*/
        break;

      default: /* '?' */
        printHelp(argv[0]);
        break;
    }
  }

  /* check input file is valid */
  if (inputfilename == NULL || option == OPTION_UNKNOWN ||
      (option == OPTION_PING && pingcount == 0)) {
    /* no option selected */
    printHelp(argv[0]);
  }

  /* open input file */
  inputfile = fopen(inputfilename, "r");

  if (inputfile == NULL) {
    fprintf(stderr, "ERROR: couldn't open input file '%s' \n", inputfilename);
    exit(EXIT_FAILURE);
  }

  /* open outputfile file */
  if (outputfilename != NULL) {
    outputfile = fopen(outputfilename, "w");
  } else {
    outputfile = stdout;
  }

  /*check if outputfile could be open if not close+error log and closing of inputfile*/
  if (outputfile == NULL) {
    fprintf(stderr, "ERROR: couldn't open output file '%s' \n", outputfilename);
    fclose(inputfile);
    exit(EXIT_FAILURE);
  }

  /* read lines from input file (max. 1023 + \0) */
  while (fgets(line, 1023, inputfile) != NULL) {

    line[strlen(line) - 1] = '\0';

    if (ntime == 1)
      start = GetTimeMilliSec();
    
    switch (option) {

      case OPTION_PING:
        fct = LoadLib("ping");

        if (fct != NULL) {
          fct(line, outputfile, pingcount, nsec);
        }

        break;

      case OPTION_NMAP:
        fct = LoadLib("nmap");

        if (fct != NULL) {
          fct(line, outputfile);
        }

        break;

      default:
        break;
    }
    
    if (ntime == 1) {
      stop = GetTimeMilliSec();
      
      fprintf(outputfile, "profile time = %d ms\n", stop - start);
    }
  }

  exit(EXIT_SUCCESS);
}
